package com.jemframework;

import java.io.IOException;
import java.io.InputStream;
import java.util.Collections;
import java.util.Map;
import java.util.Properties;
import java.util.TreeMap;
import java.util.Map.Entry;

import org.jboss.seam.contexts.Contexts;

import com.jemframework.domain.EntityFactory;
import com.jemframework.domain.EntityHomeFactory;
import com.jemframework.domain.EntityTypeNameMapping;
import com.jemframework.domain.IdentifierGeneratorFactory;
import com.jemframework.domain.delegator.DelegatorFactory;
import com.jemframework.domain.identifier.DefaultIdentifierFactory;
import com.jemframework.domain.identifier.IdentifierFactory;
import com.jemframework.domain.validator.AttributeValidatorFactory;

public class Context {

  private static Context INSTANCE = new Context();

  private static final String FILE_NAME = "context.properties";

  private static final String ENTITY_HOME_FACTORY = "EntityHomeFactory";
  private static final String IDENTIFIER_GENERATOR_FACTORY = "IdentifierGeneratorFactory";
  private static final String DELEGATOR_FACTORY = "DelegatorFactory";
  private static final String ENTITY_FACTORY = "EntityFactory";
  private static final String ENTITY_TYPE_NAME_MAPPING = "EntityTypeNameMapping";
  private static final String ATTRIBUTE_VALIDATOR_FACTORY = "AttributeValidatorFactory";
  // private static final String IDENTIFIER_FACTORY = "IdentifierFactory";

  private Map<String, Object> iInstanceMapping;

  private boolean isInitializationInProcess;

  public static Context getInstance() {
    return INSTANCE;
  }

  private Context() {
    super();
  }

  public void init() throws IOException, InstantiationException, IllegalAccessException, ClassNotFoundException {
    isInitializationInProcess = true;
    Map<String, String> theClassNameMapping = loadClassNames();
    Map<String, Object> theInstanceMapping = createInstances(theClassNameMapping);
    iInstanceMapping = Collections.unmodifiableMap(theInstanceMapping);
    initializeInstances(iInstanceMapping);
    isInitializationInProcess = false;
  }

  private Map<String, String> loadClassNames() throws IOException {

    try {

      Properties theProperties = new Properties();
      InputStream in = getClass().getClassLoader().getResourceAsStream(FILE_NAME);
      theProperties.load(in);

      Map<String, String> theClassNames = new TreeMap<String, String>();

      for (Entry<Object, Object> eachEntry : theProperties.entrySet()) {
        String theKey = (String) eachEntry.getKey();
        String theClassName = (String) eachEntry.getValue();
        theClassNames.put(theKey, theClassName);
      }

      return theClassNames;

    } catch (IOException e) {
      throw e;
    }
  }

  private Map<String, Object> createInstances(Map<String, String> aClassNameMapping) throws InstantiationException, IllegalAccessException,
      ClassNotFoundException {

    Map<String, Object> theInstanceMapping = new TreeMap<String, Object>();

    for (Entry<String, String> eachEntry : aClassNameMapping.entrySet()) {
      String theKey = eachEntry.getKey();
      String theClassName = eachEntry.getValue();
      Object theInstance = getClass().getClassLoader().loadClass(theClassName).newInstance();
      theInstanceMapping.put(theKey, theInstance);
    }

    return theInstanceMapping;
  }

  private void initializeInstances(Map<String, Object> anInstanceMapping) {

    Initializable theEntityTypeNameMapping = (Initializable) anInstanceMapping.get(ENTITY_TYPE_NAME_MAPPING);
    theEntityTypeNameMapping.initialize();

    Initializable theEntityHomeFactory = (Initializable) anInstanceMapping.get(ENTITY_HOME_FACTORY);
    theEntityHomeFactory.initialize();

    Initializable theIdentifierGeneratorFactory = (Initializable) anInstanceMapping.get(IDENTIFIER_GENERATOR_FACTORY);
    theIdentifierGeneratorFactory.initialize();

    Initializable theDelegatorFactory = (Initializable) anInstanceMapping.get(DELEGATOR_FACTORY);
    theDelegatorFactory.initialize();

    Initializable theEntityFactory = (Initializable) anInstanceMapping.get(ENTITY_FACTORY);
    theEntityFactory.initialize();

    Initializable theAttributeValidatorFactory = (Initializable) anInstanceMapping.get(ATTRIBUTE_VALIDATOR_FACTORY);
    theAttributeValidatorFactory.initialize();

    //    Initializable theIdentifierFactory = (Initializable) anInstanceMapping.get(IDENTIFIER_FACTORY);
    //    theIdentifierFactory.initialize();
  }

  protected Object getInstance(String aName) {
    return iInstanceMapping.get(aName);
  }

  public EntityHomeFactory getEntityHomeFactory() {
    EntityHomeFactory theEntityHomeFactory = (EntityHomeFactory) getInstance(ENTITY_HOME_FACTORY);
    return theEntityHomeFactory;
  }

  public IdentifierGeneratorFactory getIdentifierGeneratorFactory() {
    IdentifierGeneratorFactory theIdentifierGeneratorFactory = (IdentifierGeneratorFactory) getInstance(IDENTIFIER_GENERATOR_FACTORY);
    return theIdentifierGeneratorFactory;
  }

  public DelegatorFactory getDelegatorFactory() {
    DelegatorFactory theDelegatorFactory = (DelegatorFactory) getInstance(DELEGATOR_FACTORY);
    return theDelegatorFactory;
  }

  public AttributeValidatorFactory getAttributeValidatorFactory() {
    AttributeValidatorFactory theAttributeValidatorFactory = (AttributeValidatorFactory) getInstance(ATTRIBUTE_VALIDATOR_FACTORY);
    return theAttributeValidatorFactory;
  }

  public EntityFactory getEntityFactory() {
    EntityFactory theEntityFactory = (EntityFactory) getInstance(ENTITY_FACTORY);
    return theEntityFactory;
  }

  public EntityTypeNameMapping getEntityTypeNameMapping() {

    if (isInitializationInProcess) {
      EntityTypeNameMapping theEntityTypeNameMapping = (EntityTypeNameMapping) getInstance(ENTITY_TYPE_NAME_MAPPING);
      return theEntityTypeNameMapping;
    }

    if (!Contexts.getApplicationContext().isSet("EntityTypeNameMapping")) {
      Contexts.getApplicationContext().set("EntityTypeNameMapping", getInstance(ENTITY_TYPE_NAME_MAPPING));
    }

    return (EntityTypeNameMapping) Contexts.getApplicationContext().get("EntityTypeNameMapping");
  }

  public IdentifierFactory getIdentifierFactory() {

    if (!Contexts.getApplicationContext().isSet("IdentifierFactory")) {
      Contexts.getApplicationContext().set("IdentifierFactory", new DefaultIdentifierFactory());
    }

    return (IdentifierFactory) Contexts.getApplicationContext().get("IdentifierFactory");
  }
}
